package com.tonglin.red.service.impl;

import com.tonglin.red.entity.vo.RedPacketDto;
import com.tonglin.red.service.IRedPacketService;
import com.tonglin.red.service.IRedService;
import com.tonglin.red.util.RedPacketUtil;
import io.swagger.models.auth.In;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 发红包功能
 */
@Service
public class IRedPacketServiceImpl implements IRedPacketService {
    
    private static Logger logger = LoggerFactory.getLogger(IRedPacketServiceImpl.class);
    //存储至缓存系统Redis时定义的key前缀
    private static final String keyPrefix="redis:red:packet:";
    //定义Redis操作Bean组件
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    IRedService iRedService;

    @Override
    public String handOut(RedPacketDto dto) throws Exception {
        if (dto.getTotal()>0&&dto.getAmount()>0){
            //采用二倍均值法生成随机金额
            List<Integer> list = RedPacketUtil.divideRedPackage(dto.getAmount(),dto.getTotal());

            //生成全局唯一标识串
            String timestamp=String.valueOf(System.nanoTime());
            //根据缓存key的前缀与其他信息拼接成一个新的用户存储随机金额列表的key
            String redId = new StringBuffer(keyPrefix).append(dto.getUserId()).append(":").append(timestamp).toString();
               //将随机金额列表放入缓存列表中
            redisTemplate.opsForList().leftPushAll(redId,list);
            //根据缓存key的前缀与其他信息拼成一个新的用户存储红包总数的key
            String redTotalKey = redId+":total";
            //将红包总数存入缓存中
            redisTemplate.opsForValue().set(redTotalKey,dto.getTotal());
            System.out.println("-------"+redisTemplate.opsForValue().get(redTotalKey));
            //异步记录红包的全局唯一表示串、红包个数与随机金额列表入数据库
            iRedService.recordRedPacket(dto,redId,list);
            return redId;
        }else {
            throw new Exception("系统异常-分发红包-参数不合法");
        }

    }

    /**
     *
     *
     * @param userId  当前用户id
     * @param redId   红包全局唯一标识符
     * @return  返回抢到的红包金额或者抢不到返回金额为null
     * @throws Exception
     */
    @Override
    public BigDecimal rob(Integer userId, String redId) throws Exception {
        //定义redis操作组件的值操作方法
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //在用户抢红包之前，需要判断一下当前用户是否已经抢过该红包了
        //如果已经抢过了，则直接返回红包金额，并在前端显示出来
        Object obj = valueOperations.get(redId+userId+":rob");
        if (obj!=null){
            return new BigDecimal(obj.toString());
        }
        //点红包业务逻辑，主要用于判断缓存系统中是否仍然有红包，即
        //红包剩余个数是否大于0
        Boolean res=click(redId);
        if (res) {
//            //res为true，则可以进入”拆红包“，业务逻辑的处理
//            //首先是从小红包随机金额列表中弹出一个随机金额
//            Object value = redisTemplate.opsForList().rightPop(redId);
//            System.out.println("hahahahah"+value.toString());
//            if (value!=null){
//                //value!=null,表示当前弹出的红包金额不为null，即红包金额不为0，进而
//                //表示用户抢到 一个红包了，则可以进入后续的更新缓存与记录信息入数据库了
//                String redTotalKey = redId+":total";
//                //首先是更新缓存系统中剩余红包个数，即红包个数减一
//                Integer currTotal=valueOperations.get(redTotalKey)!=null?(Integer) valueOperations.get(redTotalKey):0;
//                valueOperations.set(redTotalKey,currTotal-1);
//                //将红包金额返回给用户前，在这里金额单元为 元，
//                //如果不设置，可以直接返回value返回，但是前端要除以100的操作，
//                BigDecimal result = new BigDecimal(value.toString()).divide(new BigDecimal(100));
//                //记录抢到红包时用户的账号信息以及抢到的金额等信息进入数据库
//                iRedService.recordRobRedPacket(userId,redId,new BigDecimal(value.toString()));
//                //将当前抢到红包的用户信息放置进缓存系统中，用于表示当前用户已经抢过红包了
//                valueOperations.set(redId+userId+":rob",result,24L, TimeUnit.HOURS);
//                //输出当前用户抢到红包的记录信息
//                logger.info("当前用户抢到红包了：userId={} key={} 金额={}",userId,redId,result);
//                //将结果返回
//                return  result;
//            }
            //加分布式锁：一个红包每个人只能抢到一次随机金额，即要保证一对一地构建缓存中的key
            final String lockKey = redId + userId + "-lock";
            //调用setIfAcsent()方法，间接实现分布式锁
            Boolean lock = valueOperations.setIfAbsent(lockKey, redId);
            //设置该分布式锁的过期时间为24小时
            redisTemplate.expire(lockKey, 24L, TimeUnit.HOURS);
            try {
                //表示当前线程获取了该分布式锁
                if (lock) {
                    //开始执行后续的业务逻辑
                    Object value = redisTemplate.opsForList().rightPop(redId);
                    System.out.println("hahahahah" + value.toString());
                    if (value != null) {
                        //value!=null,表示当前弹出的红包金额不为null，即红包金额不为0，进而
                        //表示用户抢到 一个红包了，则可以进入后续的更新缓存与记录信息入数据库了
                        String redTotalKey = redId + ":total";
                        //首先是更新缓存系统中剩余红包个数，即红包个数减一
                        Integer currTotal = valueOperations.get(redTotalKey) != null ? (Integer) valueOperations.get(redTotalKey) : 0;
                        valueOperations.set(redTotalKey, currTotal - 1);
                        //将红包金额返回给用户前，在这里金额单元为 元，
                        //如果不设置，可以直接返回value返回，但是前端要除以100的操作，
                        BigDecimal result = new BigDecimal(value.toString()).divide(new BigDecimal(100));
                        //记录抢到红包时用户的账号信息以及抢到的金额等信息进入数据库
                        iRedService.recordRobRedPacket(userId, redId, new BigDecimal(value.toString()));
                        //将当前抢到红包的用户信息放置进缓存系统中，用于表示当前用户已经抢过红包了
                        valueOperations.set(redId + userId + ":rob", result, 24L, TimeUnit.HOURS);
                        //输出当前用户抢到红包的记录信息
                        logger.info("当前用户抢到红包了：userId={} key={} 金额={}", userId, redId, result);
                        //将结果返回
                        return result;
                    }
                }
            } catch (Exception e) {
                throw new Exception("系统异常-抢红包-加分布式锁!");
            }
        }
        //null表示当前用户没有抢到红包
        return null;
    }
    private Boolean click(String redId) throws Exception{
        //定义redis的操作组件
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //定义用户查询缓存系统中红包剩余个数的key
        String redTotalKey = redId+":total";
        //获取缓存Redis中红包的个数
        Object total = valueOperations.get(redTotalKey);
        //判断红包剩余个数total是否大于0，如果大于0，则返回true，表示还有红包
        if (total!=null&&Integer.valueOf(total.toString())>0){
            return  true;
        }
        //返回false，则表示已经没有红包可以抢到了
        return false;
    }
}
